package com.atjava.webspider.logic;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;

import com.atjava.webspider.consts.Const;
import com.atjava.webspider.html.DefaultLinkParser;
import com.atjava.webspider.html.LinkParser;
import com.atjava.webspider.model.Output;
import com.atjava.webspider.model.Page;
import com.atjava.webspider.model.Replace;
import com.atjava.webspider.model.Resource;
import com.atjava.webspider.model.Site;
import com.atjava.webspider.model.Task;
import com.atjava.webspider.model.Variable;
import com.atjava.webspider.util.FileSystemUtil;
import com.atjava.webspider.util.HttpUtil;
import com.atjava.webspider.util.LogUtil;
import com.atjava.webspider.util.Md5Util;
import com.atjava.webspider.util.RegexpUtil;
import com.atjava.webspider.util.XmlUtil;

public class OutputLogic implements VarPrcsor {
	
	private Task task;
	private FileSystemUtil fsu;
	private ResourceLogic resLogic;
	private Site site;
	
	public OutputLogic(Task tsk){
		this.task = tsk;
		this.fsu = new FileSystemUtil();
		this.resLogic = new ResourceLogic(tsk);
		List<Site> sites = ConfigLogic.getSiteConfig().getSites();
		this.site = (Site)CommonLogic.getByName(sites,task.getSite());
	}
	
	public boolean isSameLevel(String url1,String url2){
		return getOutput(url1) == getOutput(url2);
	}
	
	public List<Output> getOutputByLevel(byte level){
		List<Output> lvOutputs = new ArrayList<Output>();
		List<Output> outputs = task.getOutputs();
		for(Output out: outputs){
			Page pg = (Page)CommonLogic.getByName(site.getPages(), out.getPage());
			if(level == pg.getLevel())
				lvOutputs.add(out);
		}
		return lvOutputs;
	}
	
	public Output getOutputByName(String name){
		/*
		//output
		List<Output> outputs = task.getOutputs();
		//pattern
		for(int i=0;i<outputs.size();i++){
			Output out = outputs.get(i);
			if(name.equals(out.getName()))
				return out;
		}*/
		return (Output)CommonLogic.getByName(task.getOutputs(), name);
	}
	
	public Output getOutput(String url){
		//output
		List<Output> outputs = task.getOutputs();
		//pattern
		for(int i=0;i<outputs.size();i++){
			Output out = outputs.get(i);
			String pattern = out.getUrlRegexp();
			if(pattern == null)
				continue;
			if(RegexpUtil.find(url, pattern)){
				return out;
			}
		}
		return null;
	}
	
	private String getOutputXml(URL url,String content,Output output){
		StringBuffer buf = new StringBuffer
		("<?xml version=\"1.0\" encoding=\"" + site.getCharset() + "\"?>");
		Page pg = (Page)CommonLogic.getByName(site.getPages(), output.getPage());
		List<Variable> vars = pg.getVariables();
		buf.append("\r\n<Article>\r\n");
		for(Variable var :vars ){
			String name = var.getName();
			String exp = var.getRegexp();
			int num = var.getGroupNum();
			//从content里面获得变量的值
			String value = RegexpUtil.getGroup(content, exp, num);
			String element;
			if(XmlUtil.needCdata(value)){
				element = XmlUtil.getCdataElementXml(name, value);
			}else{
				element = XmlUtil.getElementXml(name, value);
			}
			buf.append(element);
			buf.append("\r\n");
		}
		//系统变量
		VarLogic varLogic = new VarLogic(output,pg);
		buf.append(varLogic.systemVarsXml(url));
		buf.append("</Article>\r\n");
		return processValue(url, content, buf.toString(), output);
	}
	
	public String replaceText(String text){
		if(StringUtils.isBlank(text))
			return text;
		List<Replace> replaces = site.getReplaces();
		for (int x = 0;x < replaces.size(); x++) {
			Replace r = replaces.get(x);
			if(r.getReplacement() == null)
				r.setReplacement("");//JOX空值和空字符串的BUG
			text = RegexpUtil.replaceAll
			(text, r.getReplacement(), r.getRegexp(), r.getGroupNum());
		}
		return text;
	}
	
	private String getOutputTmpltText(URL url,String content,Output output)throws Exception{
		File templateFile = new File(output.getTemplate());
		if(!templateFile.exists())
			throw new Exception("Template file [ " 
					+ templateFile.getAbsolutePath() 
					+ " ] is not exist!" 
					+ output.getTemplate());
		//读取模板
		String template = fsu.readText(output.getTemplate(),site.getCharset());
		//变量和逻辑处理
		Page pg = (Page)CommonLogic.getByName(site.getPages(), output.getPage());
		VarLogic varLogic = new VarLogic(output,pg);
		//处理模板上的系统变量
		template = varLogic.systemVars(url,template);
		//用户变量
		template = varLogic.userVars(url, content, template ,this);
		return template;
	}
	
	/**
	 * 输出数据文件
	 * @param url
	 * @param content
	 * @param output
	 */
	public void output(URL url,String content,Output output){
		try {
			String text = null;
			if(output.getType() == 1){
				//输出XML
				text = getOutputXml(url, content, output);
			}else if(output.getType() == 2){
				//用模板进行输出
				text = getOutputTmpltText(url, content, output);
			}else{
				return ;
			}
			//保存文本
			saveText(url, text, output);
			//任务量加一
			task.setDataNum(task.getDataNum() + 1);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public String processValue(URL url,String content, String text,Output output) {
		//替换自定义替换
		text = replaceText(text);
		//替换本地链接，如果深度超出最大深度不进行替换
		Page pg = (Page)CommonLogic.getByName(site.getPages(), output.getPage());
		if(pg.getLevel() <= task.getMaxDepth()){
			text = replaceOtherOuputLink(url, text, output);
		}
		//获得要下载的资源
		List<Resource> resources = task.getResources();
		try {
			for (Resource resource : resources) {
				text = saveResource(url, content, text, output, resource);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return text;
	}
	
	public String getDataFileExtens(Output out){
		String subExtens;
		String tmplt = out.getTemplate();
		if(StringUtils.isBlank(tmplt)){
			subExtens = "xml";
		}else{
			subExtens = FileSystemUtil.getFileType(out.getTemplate());
		}
		return subExtens;
	}
	
	/**
	 * 替换文本上的其他满足规则的其他链接为本地链接
	 * @param url
	 * @param value
	 * @param output
	 */
	private String replaceOtherOuputLink(URL url,String text,Output output){
		LinkParser parser = DefaultLinkParser.getInstance();
		List<String> linkTags = parser.parseLink(text);
		
		for (int n = 0; n < linkTags.size(); n++) {
			String linkUrl = HttpUtil.getAbsoluteUrl(url.toString(), linkTags.get(n));
			Output subout = getOutput(linkUrl);
			if(subout == null)
				continue;
			String subExtens = getDataFileExtens(subout);
			//根据子连接地址得出子连接文件名
			String subMd5Id = Md5Util.md5String(linkUrl);
			if(output.getPath() == null)
				output.setPath("");//JOX空值和空字符串的BUG
			String outPath = getRelativeUrl(output.getPath()) + subout.getPath();
			//替换连接路径为本地路径
			text = RegexpUtil.replaceAll
			(text, linkTags.get(n), outPath + "/" + subMd5Id + "." + subExtens);
		}
		return text;
	}
	
	/**
	 * 保存资源
	 * @param url
	 * @param content 包含资源的文本
	 * @param textData 要替换链接的文本
	 * @param output
	 * @param resUrlList
	 * @throws Exception
	 */
	private String saveResource(URL url,String content,String textData,Output output,Resource res)throws Exception{
		//分析采集内容中的资源
		if(StringUtils.isNotBlank(res.getPath())){
			if(output.getPath() == null)
				output.setPath("");//JOX空值和空字符串的BUG
			String outputUrl = getRelativeUrl(output.getPath());
			
			List<String> resUrls;
			//得到某段html中所有该资源的连接
			if(res.getScope() == 0){
				//采集全文资源
				resUrls = resLogic.getResourceUrls(content, res);
			}else{
				//只采集变量的资源
				resUrls = resLogic.getResourceUrls(textData, res);
			}
			
			for (int n = 0; n < resUrls.size(); n++) {
				String resRelUrl = resUrls.get(n);
				//得到资源绝对URL路径
				String resAbsUrl = HttpUtil.getAbsoluteUrl(url.toString(), resRelUrl);
				String resType = FileSystemUtil.getFileType(resRelUrl);
				if(!FileSystemUtil.isLogicFileType(resType)){
					LogUtil.info("Illegal file type in url : " + resAbsUrl);
					continue;
				}
				
				String resNewPath = Md5Util.md5String(resAbsUrl) + "." + resType;
				String resNewUrl = outputUrl + res.getPath() + "/" + resNewPath;
				
				File resFolder = new File(getSavePath(res.getPath()));
				if (!resFolder.exists())
					resFolder.mkdirs();
				
				//输出资源
				if(outputResource(new URL(resAbsUrl),resFolder.getAbsolutePath() + "/" + resNewPath)){
					//替换资源路径
					textData = textData.replaceAll(resRelUrl,resNewUrl);
				}
			}
		}
		return textData;
	}
	
	private void saveText(URL url,String textData,Output output)throws Exception{
		//保存文章
		if(output.getPath() == null)
			output.setPath("");//JOX空值和空字符串的BUG
		String savePath = getSavePath(output.getPath());
		File outFloder = new File(savePath);
		if (!outFloder.exists())
			outFloder.mkdirs();
		String extens = getDataFileExtens(output);
		String md5FileName = Md5Util.md5String(url.toString());
		//输出XML时，文件名为   站点名称 - md5文件名
		if(output.getType() == 1){
			md5FileName = site.getName() + Const.DATA_NAME_SEPARATOR
					+ site.getCharset() + Const.DATA_NAME_SEPARATOR
					+ md5FileName;
		}
		String path = savePath + File.separator + md5FileName + "." + extens;
		OutputStream os = null;
		try {
			os = new FileOutputStream(new File(path));
			fsu.writeText(textData, os, site.getCharset());
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			FileSystemUtil.close(os);
		}
	}
	
	private boolean outputResource(URL url,String resFilePath){
		try {
			//资源地址
			HttpURLConnection connection = HttpUtil.getValidConnection(url);
			if(connection == null)
				return false;
			//网页资源流重试三次
			InputStream is = HttpUtil.getSafeInputStream(connection, 3);
			if(is == null)
				return false;
			//资源文件
			File resFile = new File(resFilePath);
			//输出
			fsu.writeData(is, resFile);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	private String getBefPath(String textPath){
		if(textPath.startsWith("/"))
			textPath = textPath.substring(1) + "/";
		if("/".equals(textPath))
			textPath = "";
		return textPath;
	}
	
	public String getRelativeUrl(String path){
		if("".equals(path))
			return "";
		path = getBefPath(path);
		String s = "";
		for (int i = 0; i < StringUtils.countMatches(path, "/")+1; i++) {
			s += "../";
		}
		return  s;
	}
	
	public String getSavePath(String relativePath){
		if(relativePath == null)
			relativePath = "";
		//保存路径
		String savePath = task.getSavePath();
		if(!savePath.endsWith("/"))
			savePath += "/";
		return savePath + relativePath;
	}

}
